// miniprogram/pages/schedule/schedule.js
Page({
    data: {
        currentTab: 'python',
        // 静态示例数据
        pythonCode: [
            `
        from datetime import datetime, date
        from apscheduler.schedulers.background import BackgroundScheduler
        from flask import Flask, request, jsonify
        from flask_sqlalchemy import SQLAlchemy
        from flask_cors import CORS
        import pandas as pd
        import os
        from dotenv import load_dotenv
        from sms_sender import send_sms
        from werkzeug.utils import secure_filename
        #引用盐值加密密码
        import bcrypt
        # 新增正则表达式库用于邮箱格式校验
        import re
        # 导入 SQLAlchemy 的 IntegrityError 异常类
        from sqlalchemy.exc import IntegrityError
        # SQLAlchemy 用于生成 SQL 函数的工具
        from sqlalchemy import func, select, or_, and_, desc, asc
        # 新增JWT相关导入
        from flask_jwt_extended import JWTManager, create_access_token, get_jwt_identity, jwt_required
        import base64
        import requests
        from server import config
        #在吐槽圈发布图片防伪造的方法中使用
        from urllib.parse import urlparse
        #排班表导入日期
        import inspect
        from datetime import timedelta
        from collections import defaultdict
        from chinese_calendar import is_workday, is_holiday,get_holiday_detail,is_in_lieu
        # ============== 添加排班员工信息接口 ==========
# ============== 更新Staff模型 ==============
class Staff(db.Model):
    __tablename__ = 'staff'
    staff_id = db.Column(db.String(20), primary_key=True, comment='员工编号（柜员号，如1001）')
    name = db.Column(db.String(50), nullable=False, comment='员工姓名')
    position = db.Column(db.String(20), nullable=False, comment='岗位（如现金柜员/普通柜员）')
    skills = db.Column(db.JSON, nullable=False, comment='技能标签（JSON数组，如["现金业务","外汇业务"]）')
    preferred_days = db.Column(db.JSON, comment='偏好休息日（JSON数组，如["周一","周五"]）')
    is_active = db.Column(db.Boolean, default=True, comment='是否在职（1=在职，0=离职）')
    creator_org_code = db.Column(db.String(9), nullable=False, comment='所属机构代码（来自employees表）')  # 新增字段

# ============== 员工管理接口（完整更新版） ==============
@app.route('/api/staff', methods=['POST'])
@jwt_required()
def create_staff():
    """
    员工信息录入接口
    ---
    tags:
      - 员工管理
    security:
      - JWT: []
    parameters:
      - name: body
        in: body
        required: true
        schema:
          type: object
          properties:
            staff_id:
              type: string
              example: "100001"
            name:
              type: string
              example: "张三"
            position:
              type: string
              example: "现金柜员"
            skills:
              type: array
              items:
                type: string
              example: ["现金业务"]
            preferred_days:
              type: array
              items:
                type: string
              example: ["周一"]
            is_active:
              type: boolean
              example: true
    responses:
      201:
        description: 员工创建成功
    """
    try:
        # ================= 身份验证 =================
        current_user = _validate_user()
        if isinstance(current_user, tuple):
            return current_user  # 返回错误响应

        # ================= 数据校验 =================
        data = request.get_json()
        validation_result = _validate_staff_data(data)
        if validation_result:
            return validation_result

        # ================= 数据持久化 =================
        try:
            new_staff = Staff(
                staff_id=data['staff_id'],
                name=data['name'].strip(),
                position=data['position'],
                skills=data['skills'],
                preferred_days=data.get('preferred_days'),
                is_active=data.get('is_active', True),
                creator_org_code=current_user.organization_code  # 自动关联机构
            )

            db.session.add(new_staff)
            db.session.commit()

            app.logger.info(f"员工创建成功: {new_staff.staff_id} 机构: {current_user.organization_code}")

            return jsonify({
                "status": "success",
                "data": _staff_response(new_staff, current_user)
            }), 201

        except IntegrityError as e:
            db.session.rollback()
            return _handle_integrity_error(e, data)
        except Exception as e:
            db.session.rollback()
            app.logger.error(f"员工创建异常: {str(e)}")
            raise

    except Exception as e:
        return _handle_general_error(e)

@app.route('/api/staff', methods=['GET'])
@jwt_required()
def get_all_staff():
    """
    获取当前机构所有员工列表
    ---
    tags:
      - 员工管理
    security:
      - JWT: []
    responses:
      200:
        description: 员工列表
    """
    try:
        current_user = _validate_user()
        if isinstance(current_user, tuple):
            return current_user

        # 仅查询本机构员工
        staff_list = Staff.query.filter_by(
            creator_org_code=current_user.organization_code
        ).all()

        return jsonify({
            "status": "success",
            "data": [_staff_response(staff, current_user) for staff in staff_list]
        }), 200

    except Exception as e:
        app.logger.error(f"获取员工列表异常: {str(e)}")
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500

@app.route('/api/staff/<string:staff_id>', methods=['GET'])
@jwt_required()
def get_staff(staff_id):
    """
    获取单个员工详情（机构隔离）
    ---
    tags:
      - 员工管理
    security:
      - JWT: []
    parameters:
      - name: staff_id
        in: path
        required: true
    responses:
      200:
        description: 员工详情
    """
    try:
        current_user = _validate_user()
        if isinstance(current_user, tuple):
            return current_user

        staff = Staff.query.filter_by(
            staff_id=staff_id,
            creator_org_code=current_user.organization_code  # 机构隔离
        ).first()

        if not staff:
            app.logger.warning(f"员工不存在或越权访问: {staff_id}")
            return jsonify({
                "status": "error",
                "code": "STAFF_NOT_FOUND",
                "message": "员工不存在"
            }), 404

        return jsonify({
            "status": "success",
            "data": _staff_detail_response(staff, current_user)
        }), 200

    except Exception as e:
        app.logger.error(f"获取员工详情异常: {str(e)}")
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500

@app.route('/api/staff/<string:staff_id>', methods=['DELETE'])
@jwt_required()
def delete_staff(staff_id):
    """
    删除员工（机构隔离）
    ---
    tags:
      - 员工管理
    security:
      - JWT: []
    parameters:
      - name: staff_id
        in: path
        required: true
    responses:
      200:
        description: 删除结果
    """
    try:
        current_user = _validate_user()
        if isinstance(current_user, tuple):
            return current_user

        staff = Staff.query.filter_by(
            staff_id=staff_id,
            creator_org_code=current_user.organization_code  # 机构隔离
        ).first()

        if not staff:
            return jsonify({
                "status": "error",
                "code": "STAFF_NOT_FOUND",
                "message": "员工不存在"
            }), 404

        db.session.delete(staff)
        db.session.commit()

        app.logger.info(f"员工删除成功: {staff_id} 机构: {current_user.organization_code}")

        return jsonify({
            "status": "success",
            "data": {
                "deleted_id": staff_id,
                "managed_org": current_user.organization_code
            }
        }), 200

    except Exception as e:
        db.session.rollback()
        app.logger.error(f"员工删除异常: {str(e)}")
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500

# ============== 辅助函数 ==============
def _validate_user():
    """统一验证用户权限"""
    current_user_id = int(get_jwt_identity())
    current_user = Employee.query.get(current_user_id)
    
    if not current_user:
        app.logger.warning(f"无效用户尝试访问: {current_user_id}")
        return jsonify({
            "status": "error",
            "code": "USER_NOT_FOUND",
            "message": "用户不存在"
        }), 404

    if current_user.job_title != '营运主管':
        app.logger.warning(f"非主管用户操作: {current_user_id}")
        return jsonify({
            "status": "error",
            "code": "PERMISSION_DENIED",
            "message": "仅限营运主管操作"
        }), 403

    if not current_user.organization_code or len(current_user.organization_code) != 9:
        app.logger.error(f"机构代码异常: {current_user.organization_code}")
        return jsonify({
            "status": "error",
            "code": "INVALID_ORG_CODE",
            "message": "无效机构代码"
        }), 400

    return current_user

def _validate_staff_data(data):
    """统一验证员工数据"""
    required_fields = ['staff_id', 'name', 'position', 'skills']
    if missing := [f for f in required_fields if f not in data]:
        return jsonify({
            "status": "error",
            "code": "MISSING_FIELDS",
            "message": f"缺少必填字段: {', '.join(missing)}"
        }), 400

    if not re.fullmatch(r'^\d{8}$', data['staff_id']):
        return jsonify({
            "status": "error",
            "code": "INVALID_ID_FORMAT",
            "message": "员工编号必须为8位数字"
        }), 400

    valid_positions = ['现金柜员', '普通柜员', '大堂经理', '客户经理']
    if data['position'] not in valid_positions:
        return jsonify({
            "status": "error",
            "code": "INVALID_POSITION",
            "message": f"无效岗位: {data['position']}"
        }), 400

    valid_skills = ['现金业务', '外汇业务', '对公业务', '信贷业务', '个人业务']
    if invalid := [s for s in data['skills'] if s not in valid_skills]:
        return jsonify({
            "status": "error",
            "code": "INVALID_SKILLS",
            "message": f"无效技能: {', '.join(invalid)}"
        }), 400

    if 'preferred_days' in data:
        valid_days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        if invalid := [d for d in data['preferred_days'] if d not in valid_days]:
            return jsonify({
                "status": "error",
                "code": "INVALID_DAYS",
                "message": f"无效休息日: {', '.join(invalid)}"
            }), 400
    return None

def _staff_response(staff, user):
    """统一员工响应格式"""
    return {
        "staff_id": staff.staff_id,
        "name": staff.name,
        "position": staff.position,
        "managed_org": user.organization_code,
        "is_active": staff.is_active
    }

def _staff_detail_response(staff, user):
    """员工详情响应格式"""
    return {
        "staff_id": staff.staff_id,
        "name": staff.name,
        "position": staff.position,
        "skills": staff.skills,
        "preferred_days": staff.preferred_days,
        "is_active": staff.is_active,
        "managed_org": user.organization_code
    }

def _handle_integrity_error(e, data):
    """处理唯一性约束错误"""
    if "Duplicate entry" in str(e.orig):
        app.logger.warning(f"重复员工编号: {data['staff_id']}")
        return jsonify({
            "status": "error",
            "code": "DUPLICATE_ID",
            "message": "员工编号已存在"
        }), 409
    app.logger.error(f"数据库错误: {str(e)}")
    return jsonify({
        "status": "error",
        "code": "DATABASE_ERROR",
        "message": "数据存储失败"
    }), 500

def _handle_general_error(e):
    """通用错误处理"""
    db.session.rollback()
    app.logger.error(f"服务器异常: {str(e)}\n{traceback.format_exc()}")
    return jsonify({
        "status": "error",
        "code": "SERVER_ERROR",
        "message": "服务器内部错误"
    }), 500

# ============== 添加排班员工信息接口 ==========

# ============== 添加排班日期信息接口 ==========
# 在模型定义区域添加RosterPlan模型
class RosterPlan(db.Model):
    __tablename__ = 'roster_plan'
    plan_id = db.Column(db.Integer, primary_key=True, comment='排班计划ID')
    creator_org_code = db.Column(db.String(9), nullable=False, comment='创建者所属机构代码')
    start_date = db.Column(db.Date, nullable=False, comment='排班周期开始日期')
    end_date = db.Column(db.Date, nullable=False, comment='排班周期结束日期')
    exclude_dates = db.Column(db.JSON, comment='排除的停业日期')
    status = db.Column(db.Boolean, default=False, comment='状态（0=草稿，1=已发布）')

# 排班计划接口
@app.route('/api/roster_plans', methods=['POST', 'GET'])
@app.route('/api/roster_plans/<int:plan_id>', methods=['GET', 'PATCH', 'DELETE'])
@jwt_required()
def manage_roster_plans(plan_id=None):
    """
    排班计划管理接口
    ---
    tags:
      - 排班管理
    security:
      - JWT: []
    """
    try:
        # ================= 通用验证 =================
        current_user = Employee.query.get(int(get_jwt_identity()))
        
        # 验证用户权限
        if not current_user or current_user.job_title != '营运主管':
            return jsonify({
                "status": "error",
                "code": "PERMISSION_DENIED",
                "message": "仅限营运主管操作"
            }), 403

        if not current_user.organization_code or len(current_user.organization_code) != 9:
            return jsonify({
                "status": "error",
                "code": "INVALID_ORG_CODE",
                "message": "机构代码无效或未配置"
            }), 400

        org_code = current_user.organization_code

        # ================= POST 创建排班计划 =================
        if request.method == 'POST':
            data = request.get_json()
            
            # 校验必填字段
            required_fields = ['start_date', 'end_date']
            if missing := [f for f in required_fields if f not in data]:
                return jsonify({
                    "status": "error",
                    "code": "MISSING_FIELDS",
                    "message": f"缺少必填字段: {', '.join(missing)}"
                }), 400

            # 日期格式校验
            try:
                start_date = datetime.strptime(data['start_date'], '%Y-%m-%d').date()
                end_date = datetime.strptime(data['end_date'], '%Y-%m-%d').date()
            except ValueError:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_DATE_FORMAT",
                    "message": "日期格式应为YYYY-MM-DD"
                }), 400

            # 日期逻辑校验
            if start_date > end_date:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_DATE_RANGE",
                    "message": "开始日期不能晚于结束日期"
                }), 400

            # 排除日期校验
            exclude_dates = []
            if 'exclude_dates' in data:
                if not isinstance(data['exclude_dates'], list):
                    return jsonify({
                        "status": "error",
                        "code": "INVALID_EXCLUDE_DATES",
                        "message": "排除日期应为数组格式"
                    }), 400

                for date_str in data['exclude_dates']:
                    try:
                        exclude_date = datetime.strptime(date_str, '%Y-%m-%d').date()
                        # 检查排除日期是否在排班周期内
                        if not (start_date <= exclude_date <= end_date):
                            return jsonify({
                                "status": "error",
                                "code": "INVALID_EXCLUDE_DATE_RANGE",
                                "message": f"排除日期 {date_str} 不在排班周期内"
                            }), 400
                        exclude_dates.append(date_str)
                    except ValueError:
                        return jsonify({
                            "status": "error",
                            "code": "INVALID_EXCLUDE_DATE",
                            "message": f"无效排除日期: {date_str}"
                        }), 400

            # 创建排班计划
            new_plan = RosterPlan(
                creator_org_code=org_code,
                start_date=start_date,
                end_date=end_date,
                exclude_dates=exclude_dates or None,
                status=data.get('status', False)
            )

            db.session.add(new_plan)
            db.session.commit()

            return jsonify({
                "status": "success",
                "data": {
                    "plan_id": new_plan.plan_id,
                    "start_date": new_plan.start_date.isoformat(),
                    "end_date": new_plan.end_date.isoformat(),
                    "exclude_dates": new_plan.exclude_dates,
                    "status": new_plan.status,
                    "total_days": (end_date - start_date).days + 1,
                    "working_days": (end_date - start_date).days + 1 - len(exclude_dates)
                }
            }), 201

        # ================= GET 获取排班计划 =================
        elif request.method == 'GET':
            # 获取单个计划
            if plan_id:
                plan = RosterPlan.query.filter_by(
                    plan_id=plan_id,
                    creator_org_code=org_code
                ).first()

                if not plan:
                    return jsonify({
                        "status": "error",
                        "code": "PLAN_NOT_FOUND",
                        "message": "排班计划不存在"
                    }), 404

                # 生成完整日期列表
                delta = plan.end_date - plan.start_date
                all_dates = [
                    (plan.start_date + timedelta(days=i)).isoformat()
                    for i in range(delta.days + 1)
                ]
                
                # 过滤排除日期
                working_dates = [
                    d for d in all_dates
                    if d not in (plan.exclude_dates or [])
                ]

                return jsonify({
                    "status": "success",
                    "data": {
                        "plan_id": plan.plan_id,
                        "start_date": plan.start_date.isoformat(),
                        "end_date": plan.end_date.isoformat(),
                        "exclude_dates": plan.exclude_dates,
                        "status": plan.status,
                        "total_days": len(all_dates),
                        "working_days": len(working_dates),
                        "all_dates": all_dates,
                        "working_dates": working_dates
                    }
                })

            # 获取计划列表
            page = request.args.get('page', 1, type=int)
            per_page = request.args.get('per_page', 10, type=int)

            query = RosterPlan.query.filter_by(creator_org_code=org_code)
            pagination = query.order_by(RosterPlan.start_date.desc()).paginate(
                page=page, per_page=per_page)

            return jsonify({
                "status": "success",
                "data": {
                    "items": [{
                        "plan_id": plan.plan_id,
                        "start_date": plan.start_date.isoformat(),
                        "end_date": plan.end_date.isoformat(),
                        "status": plan.status,
                        "exclude_count": len(plan.exclude_dates) if plan.exclude_dates else 0
                    } for plan in pagination.items],
                    "pagination": {
                        "page": pagination.page,
                        "per_page": pagination.per_page,
                        "total": pagination.total
                    }
                }
            })

        # ================= PATCH 更新排班计划 =================
        elif request.method == 'PATCH':
            plan = RosterPlan.query.filter_by(
                plan_id=plan_id,
                creator_org_code=org_code
            ).first()

            if not plan:
                return jsonify({
                    "status": "error",
                    "code": "PLAN_NOT_FOUND",
                    "message": "排班计划不存在"
                }), 404

            data = request.get_json()
            updates = {}

            # 更新排除日期
            if 'exclude_dates' in data:
                if not isinstance(data['exclude_dates'], list):
                    return jsonify({
                        "status": "error",
                        "code": "INVALID_EXCLUDE_DATES",
                        "message": "排除日期应为数组格式"
                    }), 400

                valid_dates = []
                for date_str in data['exclude_dates']:
                    try:
                        exclude_date = datetime.strptime(date_str, '%Y-%m-%d').date()
                        # 检查日期是否在计划周期内
                        if plan.start_date <= exclude_date <= plan.end_date:
                            valid_dates.append(date_str)
                    except:
                        pass  # 自动过滤无效日期
                updates['exclude_dates'] = valid_dates or None

            # 更新状态
            if 'status' in data:
                if not isinstance(data['status'], bool):
                    return jsonify({
                        "status": "error",
                        "code": "INVALID_STATUS",
                        "message": "状态值无效"
                    }), 400
                updates['status'] = data['status']

            # 执行更新
            for key, value in updates.items():
                setattr(plan, key, value)

            db.session.commit()

            return jsonify({
                "status": "success",
                "data": {
                    "plan_id": plan.plan_id,
                    "updated_fields": list(updates.keys())
                }
            })

        # ================= DELETE 删除排班计划 =================
        elif request.method == 'DELETE':
            plan = RosterPlan.query.filter_by(
                plan_id=plan_id,
                creator_org_code=org_code
            ).first()

            if not plan:
                return jsonify({
                    "status": "error",
                    "code": "PLAN_NOT_FOUND",
                    "message": "排班计划不存在"
                }), 404

            db.session.delete(plan)
            db.session.commit()

            return jsonify({
                "status": "success",
                "message": "排班计划已删除"
            })

    except Exception as e:
        db.session.rollback()
        app.logger.error(f"排班计划操作失败: {str(e)}")
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500
# ============== 添加排班日期信息接口结束 =======

# ============== 添加排班结果信息接口开始 =======
# 在现有模型下方添加RosterDetail模型
class RosterDetail(db.Model):
    __tablename__ = 'roster_detail'
    detail_id = db.Column(db.Integer, primary_key=True, comment='排班明细ID')
    plan_id = db.Column(db.Integer, db.ForeignKey('roster_plan.plan_id'), nullable=False)
    # staff_id = db.Column(db.String(20), db.ForeignKey('staff.staff_id'), nullable=False)
    staff_id = db.Column(db.String(20), db.ForeignKey('staff.staff_id', ondelete='CASCADE'))
    work_date = db.Column(db.Date, nullable=False, comment='工作日期')
    shift_type = db.Column(db.String(20), nullable=False, comment='班次类型')
    
    # 定义关系（可选）
    plan = db.relationship('RosterPlan', backref='details')
    staff = db.relationship('Staff', backref='schedules')

@app.route('/api/roster_details', methods=['POST', 'GET'])
@app.route('/api/roster_details/<int:detail_id>', methods=['GET', 'PATCH', 'DELETE'])
@jwt_required()
def manage_roster_details(detail_id=None):
    """
    排班结果管理接口
    ---
    tags:
      - 排班管理
    security:
      - JWT: []
    """
    try:
        # ================= 通用验证 =================
        current_user = Employee.query.get(int(get_jwt_identity()))
        
        # 验证用户权限
        if not current_user or current_user.job_title != '营运主管':
            return jsonify({
                "status": "error",
                "code": "PERMISSION_DENIED",
                "message": "仅限营运主管操作"
            }), 403

        if not current_user.organization_code or len(current_user.organization_code) != 9:
            return jsonify({
                "status": "error",
                "code": "INVALID_ORG_CODE",
                "message": "机构代码无效或未配置"
            }), 400

        org_code = current_user.organization_code

        # ================= POST 创建排班明细 =================
        if request.method == 'POST':
            data = request.get_json()
            
            # 必填字段验证
            required_fields = ['plan_id', 'staff_id', 'work_date', 'shift_type']
            if missing := [f for f in required_fields if f not in data]:
                return jsonify({
                    "status": "error",
                    "code": "MISSING_FIELDS",
                    "message": f"缺少必填字段: {', '.join(missing)}"
                }), 400

            # 验证排班计划有效性
            plan = RosterPlan.query.filter_by(
                plan_id=data['plan_id'],
                creator_org_code=org_code  # 确保查询字段与模型定义一致
            ).first()
            if not plan:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_PLAN",
                    "message": "排班计划不存在或权限不足"
                }), 404

            # 验证员工有效性
            staff = Staff.query.filter_by(
                staff_id=data['staff_id']
            ).first()
            if not staff:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_STAFF",
                    "message": "员工不存在"
                }), 404

            # 日期格式验证
            try:
                work_date = datetime.strptime(data['work_date'], '%Y-%m-%d').date()
            except ValueError:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_DATE_FORMAT",
                    "message": "日期格式应为YYYY-MM-DD"
                }), 400

            # 验证工作日期是否有效
            if not (plan.start_date <= work_date <= plan.end_date):
                return jsonify({
                    "status": "error",
                    "code": "DATE_OUT_OF_RANGE",
                    "message": "工作日期不在排班周期内"
                }), 400

            # 检查是否为排除日期
            if data['work_date'] in (plan.exclude_dates or []):
                return jsonify({
                    "status": "error",
                    "code": "EXCLUDED_DATE",
                    "message": "该日期已被标记为停业日"
                }), 400

            # 班次类型验证
            valid_shifts = ['早班', '晚班', '休息']
            if data['shift_type'] not in valid_shifts:
                return jsonify({
                    "status": "error",
                    "code": "INVALID_SHIFT_TYPE",
                    "message": f"无效班次类型，可选值: {', '.join(valid_shifts)}"
                }), 400

            # 检查是否重复排班
            existing = RosterDetail.query.filter_by(
                staff_id=data['staff_id'],
                work_date=work_date
            ).first()
            if existing:
                return jsonify({
                    "status": "error",
                    "code": "DUPLICATE_SCHEDULE",
                    "message": "该员工当日已有排班记录"
                }), 409

            # 创建排班明细
            new_detail = RosterDetail(
                plan_id=data['plan_id'],
                staff_id=data['staff_id'],
                work_date=work_date,
                shift_type=data['shift_type']
            )

            db.session.add(new_detail)
            db.session.commit()

            return jsonify({
                "status": "success",
                "data": {
                    "detail_id": new_detail.detail_id,
                    "plan_id": new_detail.plan_id,
                    "staff_id": new_detail.staff_id,
                    "work_date": new_detail.work_date.isoformat(),
                    "shift_type": new_detail.shift_type
                }
            }), 201

        # ================= GET 获取排班明细 =================
        elif request.method == 'GET':
            # 获取单个明细
            if detail_id:
                detail = RosterDetail.query.get(detail_id)
                if not detail or detail.plan.creator_org_code != org_code:
                    return jsonify({
                        "status": "error",
                        "code": "DETAIL_NOT_FOUND",
                        "message": "排班明细不存在"
                    }), 404

                return jsonify({
                    "status": "success",
                    "data": {
                        "detail_id": detail.detail_id,
                        "plan_id": detail.plan_id,
                        "staff_id": detail.staff_id,
                        "work_date": detail.work_date.isoformat(),
                        "shift_type": detail.shift_type,
                        "staff_name": detail.staff.name,
                        "position": detail.staff.position
                    }
                })

            # 获取明细列表（支持分页和过滤）
            page = request.args.get('page', 1, type=int)
            per_page = request.args.get('per_page', 20, type=int)
            plan_id = request.args.get('plan_id')
            staff_id = request.args.get('staff_id')

            # 修复查询：显式指定join条件并添加机构过滤
            base_query = db.session.query(RosterDetail).join(
                RosterPlan,
                RosterDetail.plan_id == RosterPlan.plan_id
            ).filter(
                RosterPlan.creator_org_code == org_code
            )

            # 添加过滤条件
            if plan_id:
                base_query = base_query.filter(RosterDetail.plan_id == plan_id)
            if staff_id:
                base_query = base_query.filter(RosterDetail.staff_id == staff_id)

            # 执行分页查询
            pagination = base_query.order_by(RosterDetail.work_date.asc()).paginate(
                page=page, per_page=per_page)

            return jsonify({
                "status": "success",
                "data": {
                    "items": [{
                        "detail_id": d.detail_id,
                        "plan_id": d.plan_id,
                        "staff_id": d.staff_id,
                        "work_date": d.work_date.isoformat(),
                        "shift_type": d.shift_type,
                        "staff_name": d.staff.name
                    } for d in pagination.items],
                    "pagination": {
                        "page": pagination.page,
                        "per_page": pagination.per_page,
                        "total": pagination.total
                    }
                }
            })

        # ================= PATCH 更新排班明细 =================
        elif request.method == 'PATCH':
            detail = RosterDetail.query.get(detail_id)
            if not detail or detail.plan.creator_org_code != org_code:
                return jsonify({
                    "status": "error",
                    "code": "DETAIL_NOT_FOUND",
                    "message": "排班明细不存在"
                }), 404

            data = request.get_json()
            updates = {}

            # 更新班次类型
            if 'shift_type' in data:
                valid_shifts = ['早班', '晚班', '休息']
                if data['shift_type'] not in valid_shifts:
                    return jsonify({
                        "status": "error",
                        "code": "INVALID_SHIFT_TYPE",
                        "message": f"无效班次类型，可选值: {', '.join(valid_shifts)}"
                    }), 400
                updates['shift_type'] = data['shift_type']

            # 更新其他字段（如需）
            # ...

            # 执行更新
            for key, value in updates.items():
                setattr(detail, key, value)

            db.session.commit()

            return jsonify({
                "status": "success",
                "data": {
                    "detail_id": detail.detail_id,
                    "updated_fields": list(updates.keys())
                }
            })

        # ================= DELETE 删除排班明细 =================
        elif request.method == 'DELETE':
            detail = RosterDetail.query.get(detail_id)
            if not detail or detail.plan.creator_org_code != org_code:
                return jsonify({
                    "status": "error",
                    "code": "DETAIL_NOT_FOUND",
                    "message": "排班明细不存在"
                }), 404

            db.session.delete(detail)
            db.session.commit()

            return jsonify({
                "status": "success",
                "message": "排班明细已删除"
            })

    except Exception as e:
        db.session.rollback()
        app.logger.error(f"排班明细操作失败: {str(e)}", exc_info=True)
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500

# ============== 添加排班结果信息接口结束 =======

# ============== 排班规则模型及接口 ==============
# ============== 排班规则模型及接口 ==============
class RosterRule(db.Model):
    __tablename__ = 'roster_rules'
    rule_id = db.Column(db.Integer, primary_key=True, comment='规则ID')
    rule_name = db.Column(db.String(50), nullable=False, comment='规则名称')
    rule_type = db.Column(db.Enum('hard', 'soft'), nullable=False, comment='规则类型')
    target_role = db.Column(db.String(20), comment='适用岗位')
    condition = db.Column(db.JSON, nullable=False, comment='规则条件')
    priority = db.Column(db.Integer, default=100, comment='优先级')

class HolidayCalendar(db.Model):
    __tablename__ = 'holiday_calendar'
    date = db.Column(db.Date, primary_key=True, comment='日期')
    is_holiday = db.Column(db.Boolean, default=True, comment='是否节假日')
    description = db.Column(db.String(100), comment='日期描述')

class RosterGenerator:
    def __init__(self, plan_id):
        self.plan = db.session.get(RosterPlan, plan_id)
        self.rules = self._load_rules()
        self.staff_pool = self._load_staff()
        self.schedule = defaultdict(list)
        self.conflicts = []
        self.weekly_records = defaultdict(lambda: defaultdict(lambda: {'work_days': 0, 'rest_days': 0}))
        self._prepare_staff_groups()
        
        app.logger.info(f"📅 排班周期：{self.plan.start_date} 至 {self.plan.end_date}")
        app.logger.debug(f"排除日期：{self.plan.exclude_dates}")

    def _select_client_manager(self, date):
        """客户经理轮班（按周循环）"""
        week_num = date.isocalendar()[1]  # ISO周数
        index = week_num % len(self.role_groups['cm'])
        app.logger.debug(f"  可用客户经理列表：{[cm.name for cm in self.role_groups['cm']]}")
        return self.role_groups['cm'][index]
    
    def _check_workday_limit(self, staff, date):
        """检查本周工作天数是否超过5天（修复周数计算）"""
        week_key = self._get_week_key(date)
        current_work_days = self.weekly_records[staff.staff_id][week_key]['work_days']
        app.logger.debug(f"  检查 {staff.name} 工作天数 | 本周已工作：{current_work_days}天")
        return current_work_days < 5

    def _select_public_staff(self, date):
        """选择可用的对公岗位员工"""
        app.logger.debug(f"  对公岗位候选：{[s.name for s in self.role_groups['public']]}")
        return next(
            (s for s in self.role_groups['public'] 
             if self._check_workday_limit(s, date) and self._is_workday(date, s)),
            None
        )

    def _prepare_staff_groups(self):
        """预分组员工到各岗位类别"""
        self.role_groups = {
            'cash': [],    # 现金类
            'public': [],  # 对公类
            'cm': []       # 客户经理
        }
        
        for staff in self.staff_pool:
            if staff.position == '现金柜员' or (staff.position == '普通柜员' and '现金业务' in staff.skills):
                self.role_groups['cash'].append(staff)
            elif staff.position == '普通柜员' and '对公业务' in staff.skills:
                self.role_groups['public'].append(staff)
            elif staff.position == '客户经理':
                self.role_groups['cm'].append(staff)

    def _is_workday(self, date, staff=None):
        """精确化工作日判断（四层验证）"""
        date_str = date.isoformat()
        
        # 第一层：全局排除日期（最高优先级）
        if date_str in self.plan.exclude_dates:
            app.logger.debug(f"⛔ 全局排除日期：{date_str}")
            return False
        
        # 第二层：基础工作日判断
        base_workday = is_workday(date)
        
        # 第三层：岗位类型判断
        if staff:
            staff_role = self._determine_staff_role(staff)
            weekday = date.weekday()
            
            # 对公/信贷岗位周末强制休息
            if staff_role in ['对公类', '信贷业务经理'] and weekday >= 5:
                app.logger.debug(f"⏹️ {staff.name} 对公/信贷岗位周末强制休息")
                return False
                
            # 现金/个人业务岗位跟随基础判断
            if staff_role in ['现金类', '个人业务经理']:
                app.logger.debug(f"🌓 {staff.name} 周末状态：{'可排班' if base_workday else '休息'}")
                return base_workday
        
        # 第四层：最终返回基础判断
        return base_workday

    def _determine_staff_role(self, staff):
        """精确岗位分类（版本2.0）"""
        if staff.position == '现金柜员' or (staff.position == '普通柜员' and '现金业务' in staff.skills):
            return '现金类'
        elif staff.position == '普通柜员' and '对公业务' in staff.skills:
            return '对公类'
        elif staff.position == '客户经理':
            if '信贷业务' in staff.skills:
                return '信贷业务经理'
            elif '个人业务' in staff.skills:
                return '个人业务经理'
            else:
                return '通用客户经理'
        return '其他岗位'

    def _get_week_key(self, date):
        """生成周维度键值（修复ISO周数）"""
        iso_year, iso_week, _ = date.isocalendar()
        return f"{iso_year}-W{iso_week:02d}"

    def _load_rules(self):
        return RosterRule.query.order_by(RosterRule.priority.asc()).all()

    def _load_staff(self):
        return Staff.query.filter_by(
            is_active=True,
            creator_org_code=self.plan.creator_org_code
        ).all()

    def generate(self):
        try:
            current_date = self.plan.start_date
            while current_date <= self.plan.end_date:
                date_str = current_date.isoformat()
                
                if date_str in self.plan.exclude_dates:
                    self._mark_rest_day(current_date)
                    current_date += timedelta(days=1)
                    continue
                    
                self._process_daily_schedule(current_date)
                current_date += timedelta(days=1)
            
            return self._format_result()
        
        except Exception as e:
            app.logger.error(f"排班核心逻辑异常: {str(e)}", exc_info=True)
            return {
                'schedule': {},
                'conflicts': [{
                    "type": "system_error",
                    "message": f"系统内部错误: {str(e)}"
                }]
            }

    def _mark_rest_day(self, date):
        """标记排除日为休息日（修复调用方式）"""
        for staff in self.staff_pool:
            self._assign_shift(staff, date, '休息')
            app.logger.debug(f"⛔ {staff.name} 在排除日自动休息")

    def _process_daily_schedule(self, date):
        date_str = date.isoformat()
        weekday_cn = ['周一','周二','周三','周四','周五','周六','周日'][date.weekday()]
        
        app.logger.debug(f"\n{'━'*30} 开始处理 {date_str} ({weekday_cn}) {'━'*30}")

        try:
            # 客户经理排班
            app.logger.debug("👔 正在安排客户经理...")
            cm = self._select_client_manager(date)
            app.logger.info(f"  选定客户经理：{cm.name} (ID:{cm.staff_id})")
            self._assign_shift(cm, date, '早班')
            
            # 现金岗位处理
            app.logger.debug("\n💰 正在安排现金岗位（至少2人）：")
            cash_selected = []
            for idx, staff in enumerate(self.role_groups['cash'], 1):
                status = "✅ 可排班" if self._check_workday_limit(staff, date) else "⛔ 已达上限"
                app.logger.debug(f"  候选 {idx}: {staff.name} | {status}")
                
                if len(cash_selected) >= 2: 
                    app.logger.debug("  已达2人要求，停止选择")
                    break
                    
                if self._check_workday_limit(staff, date):
                    cash_selected.append(staff)
                    self._assign_shift(staff, date, '早班')
                    app.logger.debug(f"  ↳ 已选择 {staff.name}")
            
            app.logger.info(f"✔️ 现金岗位最终安排：{len(cash_selected)}人")

            # 对公岗位处理
            app.logger.debug("\n📊 正在安排对公岗位...")
            public_staff = self._select_public_staff(date)
            if public_staff:
                app.logger.info(f"  选定对公岗位：{public_staff.name}")
                self._assign_shift(public_staff, date, '早班')
            else:
                app.logger.warning("  ⚠️ 未找到可用对公岗位员工")
                self.conflicts.append({
                    "date": date_str,
                    "type": "no_public_staff",
                    "message": "对公岗位无可用员工"
                })

            # 处理强制休息
            app.logger.debug("\n🛌 开始处理强制休息：")
            self._process_rest_days(date)

        except Exception as e:
            app.logger.error(f"排班流程异常：{str(e)}", exc_info=True)
            self.conflicts.append({
                "date": date_str,
                "type": "schedule_error",
                "message": f"排班流程异常：{str(e)}"
            })
        
        app.logger.debug(f"{'━'*30} 当日处理完成 {'━'*30}\n")

    def _process_rest_days(self, date):
        """精准化休息日处理（修复周剩余天数计算）"""
        week_key = self._get_week_key(date)
        current_weekday = date.weekday()
        remaining_days_in_week = 6 - current_weekday  # 本周剩余天数

        for staff in self.staff_pool:
            if any(s['staff_id'] == staff.staff_id for s in self.schedule[date]):
                continue
                
            staff_role = self._determine_staff_role(staff)
            
            # 对公/信贷岗位周末强制休息
            if staff_role in ['对公类', '信贷业务经理'] and date.weekday() >= 5:
                app.logger.debug(f"⏹️ 对公/信贷岗位强制休息：{staff.name}")
                self._assign_shift(staff, date, '休息')
                continue
                
            # 常规休息逻辑
            required_rest = 2 - self.weekly_records[staff.staff_id][week_key]['rest_days']
            if required_rest >= remaining_days_in_week:
                app.logger.warning(f"⚠️ 强制休息：{staff.name}（需{required_rest}天/剩{remaining_days_in_week}天）")
                self._assign_shift(staff, date, '休息')

    def _assign_shift(self, staff, date, shift):
        self.schedule[date].append({
            'staff_id': staff.staff_id,
            'shift': shift
        })
        app.logger.debug(f"分配成功: {date} {staff.staff_id} {shift}")
        
        week_key = self._get_week_key(date)
        if shift == '休息':
            self.weekly_records[staff.staff_id][week_key]['rest_days'] += 1
        else:
            self.weekly_records[staff.staff_id][week_key]['work_days'] += 1

    def _format_result(self):
        """格式化排班结果（修复日期类型判断）"""
        formatted_schedule = {}
        for date_obj, shifts in self.schedule.items():
            # 更安全的日期格式转换
            if hasattr(date_obj, 'isoformat'):
                date_str = date_obj.isoformat()
            else:
                date_str = str(date_obj)
                
            formatted_schedule[date_str] = []
            for shift_info in shifts:
                formatted_schedule[date_str].append({
                    'staff_id': shift_info['staff_id'],
                    'shift': shift_info['shift']
                })
        return {
            'schedule': formatted_schedule,
            'conflicts': self.conflicts
        }
    # ============== 智能排班接口（修正版） ==============
# ============== 智能排班生成接口（最终修复版） ==============
# ============== 智能排班生成接口（完整稳定版）==============
@app.route('/api/roster_plans/<int:plan_id>/generate', methods=['POST'])
@jwt_required()
def generate_roster(plan_id):
    """
    智能排班生成接口
    ---
    tags:
      - 排班管理
    parameters:
      - name: force
        in: query
        type: boolean
        default: false
        description: 是否强制覆盖已有排班
    responses:
      200:
        description: 排班生成结果
        schema:
          type: object
          properties:
            success:
              type: integer
            conflicts:
              type: array
            time_used:
              type: string
            night_shift_count:
              type: integer
    """
    try:
        # ================= 初始化日志 =================
        start_time = datetime.now()
        app.logger.info(f"🚀 开始生成排班计划 {plan_id}")

        # ================= 权限验证 =================
        current_user_id = int(get_jwt_identity())
        current_user = db.session.get(Employee, current_user_id)
        
        # 验证用户存在性
        if not current_user:
            app.logger.error(f"❌ 无效用户尝试访问: {current_user_id}")
            return jsonify({
                "status": "error",
                "code": "USER_NOT_FOUND",
                "message": "用户不存在"
            }), 404

        # 验证岗位权限
        if current_user.job_title != '营运主管':
            app.logger.warning(f"⛔ 非主管用户尝试生成排班: {current_user_id}")
            return jsonify({
                "status": "error",
                "code": "PERMISSION_DENIED",
                "message": "仅限营运主管操作"
            }), 403

        # 验证机构代码
        org_code = current_user.organization_code
        if not org_code or len(org_code) != 9:
            app.logger.error(f"❌ 无效机构代码: {org_code}")
            return jsonify({
                "status": "error",
                "code": "INVALID_ORG_CODE",
                "message": "机构代码无效或未配置"
            }), 400

        # ================= 获取排班计划 =================
        plan = db.session.get(RosterPlan, plan_id)
        if not plan or plan.creator_org_code != org_code:
            app.logger.error(f"❌ 排班计划不存在或权限不足: {plan_id}")
            return jsonify({
                "status": "error",
                "code": "PLAN_NOT_FOUND",
                "message": "排班计划不存在或权限不足"
            }), 404

        # ================= 强制覆盖处理 =================
        force = request.args.get('force', 'false').lower() == 'true'
        existing_count = db.session.query(RosterDetail).filter_by(plan_id=plan_id).count()
        
        if existing_count > 0:
            if not force:
                app.logger.info(f"⚠️ 检测到已有排班记录: {existing_count}条")
                return jsonify({
                    "status": "error",
                    "code": "EXISTING_SCHEDULE",
                    "message": "存在历史排班记录，请确认是否覆盖"
                }), 409
            else:
                db.session.query(RosterDetail).filter_by(plan_id=plan_id).delete()
                db.session.commit()
                app.logger.info(f"♻️ 已强制清除历史排班: {existing_count}条")

        # ================= 生成排班核心逻辑 =================
        try:
            # 初始化排班生成器
            generator = RosterGenerator(plan_id)
            
            # 执行排班生成（核心逻辑）
            generation_result = generator.generate()
            
            # 转换排班结果为数据库格式
            batch_data = []
            for date_str, shifts in generation_result['schedule'].items():  # 明确变量名为date_str
                for shift_info in shifts:
                    batch_data.append({
                        "plan_id": plan_id,
                        "staff_id": shift_info['staff_id'],
                        "work_date": date_str,  # 直接使用格式化后的字符串
                        "shift_type": shift_info['shift']
                    })

            # 分批插入数据（每500条提交一次）
            total_inserted = 0
            for i in range(0, len(batch_data), 500):
                chunk = batch_data[i:i+500]
                db.session.bulk_insert_mappings(RosterDetail, chunk)
                db.session.commit()
                total_inserted += len(chunk)
                app.logger.info(f"📥 已插入批次数据：{len(chunk)}条（累计{total_inserted}）")

            # 处理冲突信息
            if generation_result['conflicts']:
                app.logger.warning(f"⚠️ 发现排班冲突 {len(generation_result['conflicts'])} 条")
                for conflict in generation_result['conflicts']:
                    app.logger.warning(
                        f"冲突日期：{conflict.get('date')} "
                        f"类型：{conflict.get('type')} "
                        f"详情：{conflict.get('message')}"
                    )

            # 最终验证
            night_shift_count = sum(1 for item in batch_data if item['shift_type'] == '晚班')
            app.logger.info(f"🔍 最终统计 - 晚班数量：{night_shift_count}")

        except Exception as e:
            app.logger.error(f"❌ 排班生成失败: {str(e)}", exc_info=True)
            db.session.rollback()
            return jsonify({
                "status": "error",
                "code": "GENERATION_FAILED",
                "message": f"排班生成失败: {str(e)}"
            }), 500

        # ================= 更新计划状态 =================
        try:
            plan.status = True
            plan.last_generated = datetime.now()
            db.session.commit()
            app.logger.info(f"✅ 计划状态已更新：{plan_id}")
        except Exception as e:
            db.session.rollback()
            app.logger.error(f"❌ 状态更新失败: {str(e)}")
            return jsonify({
                "status": "error",
                "code": "STATUS_UPDATE_FAILED",
                "message": "计划状态更新失败"
            }), 500

        # ================= 最终结果返回 =================
        duration = (datetime.now() - start_time).total_seconds()
        app.logger.info(f"🎉 排班生成完成 总计生成: {total_inserted}条 耗时: {duration:.2f}秒")

        return jsonify({
            "status": "success",
            "data": {
                "success": total_inserted,
                "conflicts": generation_result.get('conflicts', []),
                "time_used": f"{duration:.2f}秒",
                "night_shift_count": night_shift_count
            }
        })

    except Exception as e:
        app.logger.error(f"💥 接口异常: {str(e)}", exc_info=True)
        return jsonify({
            "status": "error",
            "code": "SERVER_ERROR",
            "message": "服务器内部错误"
        }), 500

# # ============== 排班规则模型及接口结束 ==============
`

        ],
        mysqlCode: [
            "# 员工信息表",
            `-- bank_customers.staff definition

            CREATE TABLE \`staff\` (
              \`staff_id\` varchar(20) NOT NULL COMMENT '员工编号（柜员号，如1001）',
              \`name\` varchar(50) NOT NULL COMMENT '员工姓名',
              \`position\` varchar(20) NOT NULL COMMENT '岗位（如现金柜员/普通柜员）',
              \`skills\` json NOT NULL COMMENT '技能标签（JSON数组，如["现金业务","外汇业务"]）',
              \`preferred_days\` json DEFAULT NULL COMMENT '偏好休息日（JSON数组，如["周一","周五"]）',
              \`is_active\` tinyint(1) DEFAULT '1' COMMENT '是否在职（1=在职，0=离职）',
              \`creator_org_code\` char(9) NOT NULL,
              PRIMARY KEY (\`staff_id\`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='网点员工信息表';`,
            "# 排班计划表",
            `-- bank_customers.roster_plan definition

            CREATE TABLE \`roster_plan\` (
              \`plan_id\` int NOT NULL AUTO_INCREMENT COMMENT '排班计划ID',
              \`creator_org_code\` char(9) NOT NULL COMMENT '创建者所属机构代码（来自employees表）',
              \`start_date\` date NOT NULL COMMENT '排班周期开始日期',
              \`end_date\` date NOT NULL COMMENT '排班周期结束日期',
              \`exclude_dates\` json DEFAULT NULL COMMENT '排除的停业日期（JSON数组，如["2023-10-01","2023-10-02"]）',
              \`status\` tinyint(1) DEFAULT '0' COMMENT '状态（0=草稿，1=已发布）',
              PRIMARY KEY (\`plan_id\`)
            ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排班计划主表（记录排班周期和停业日）';`,
            "# 排班规则表",
            `-- bank_customers.roster_rules definition

            CREATE TABLE \`roster_rules\` (
              \`rule_id\` int NOT NULL AUTO_INCREMENT COMMENT '规则ID',
              \`rule_name\` varchar(50) NOT NULL COMMENT '规则名称（如"最大连续工作日"）',
              \`rule_type\` enum('hard','soft') NOT NULL COMMENT '规则类型（硬约束/软约束）',
              \`target_role\` varchar(20) DEFAULT NULL COMMENT '适用岗位（如"现金柜员"，NULL表示全员）',
              \`condition\` json NOT NULL COMMENT '规则条件（JSON格式，如{"operator": "<=", "threshold": 5}）',
              \`priority\` int DEFAULT '100' COMMENT '优先级（硬约束>软约束，值越小优先级越高）',
              PRIMARY KEY (\`rule_id\`)
            ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排班规则表';`,
            "# 排班结果表",
            `-- bank_customers.roster_detail definition

            CREATE TABLE \`roster_detail\` (
              \`detail_id\` int NOT NULL AUTO_INCREMENT COMMENT '排班明细ID',
              \`plan_id\` int NOT NULL COMMENT '关联排班计划ID',
              \`staff_id\` varchar(20) NOT NULL COMMENT '员工编号',
              \`work_date\` date NOT NULL COMMENT '工作日期',
              \`shift_type\` varchar(20) NOT NULL COMMENT '班次类型（如早班/晚班/休息）',
              PRIMARY KEY (\`detail_id\`),
              KEY \`plan_id\` (\`plan_id\`),
              KEY \`staff_id\` (\`staff_id\`),
              CONSTRAINT \`roster_detail_ibfk_1\` FOREIGN KEY (\`plan_id\`) REFERENCES \`roster_plan\` (\`plan_id\`),
              CONSTRAINT \`roster_detail_ibfk_2\` FOREIGN KEY (\`staff_id\`) REFERENCES \`staff\` (\`staff_id\`)
            ) ENGINE=InnoDB AUTO_INCREMENT=789 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排班结果明细表（记录每日排班）';`,
            "# 节假日名单",
            `-- bank_customers.holiday_calendar definition

            CREATE TABLE \`holiday_calendar\` (
              \`date\` date NOT NULL COMMENT '日期',
              \`is_holiday\` tinyint(1) DEFAULT NULL COMMENT '是否节假日',
              \`description\` varchar(100) DEFAULT NULL COMMENT '日期描述',
              PRIMARY KEY (\`date\`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`
          ],
    },

    switchTab(e) {
        this.setData({
            currentTab: e.currentTarget.dataset.tab
        })
    }
})